import logging
import os
import sys
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
from pathlib import Path
from typing import Any, Dict, Optional

import yaml


class LoggerConfig:
    """日志配置管理类，支持从配置文件加载参数"""

    DEFAULT_CONFIG = {
        "log_level": "INFO",
        "log_format": "%(asctime)s - %(name)s - %(module)s:%(lineno)d - %(levelname)s - %(message)s",
        "date_format": "%Y-%m-%d %H:%M:%S",
        "log_dir": "../../../logs",
        "max_bytes": 10485760,  # 10MB
        "backup_count": 10,
        "rotate_when": "D",
        "rotate_interval": 1,
        "console_output": True
    }

    def __init__(self, config_path: Optional[str] = None):
        self.config = self.DEFAULT_CONFIG.copy()
        if config_path and os.path.exists(config_path):
            self._load_from_file(config_path)

    def _load_from_file(self, config_path: str) -> None:
        """从YAML配置文件加载日志配置"""
        try:
            with open(config_path, "r", encoding="utf-8") as f:
                config = yaml.safe_load(f)
                if config and isinstance(config, dict):
                    self.config.update(config)
        except Exception as e:
            print(f"加载日志配置文件失败: {e}, 使用默认配置", file=sys.stderr)

    @property
    def log_level(self) -> int:
        """将日志级别字符串转换为logging常量"""
        level_map = {
            "DEBUG": logging.DEBUG,
            "INFO": logging.INFO,
            "WARNING": logging.WARNING,
            "ERROR": logging.ERROR,
            "CRITICAL": logging.CRITICAL
        }
        return level_map.get(self.config["log_level"].upper(), logging.INFO)


class LoggerFactory:
    """日志工厂类，负责创建和管理日志实例"""
    _loggers: Dict[str, logging.Logger] = {}
    _config: Optional[LoggerConfig] = None

    @classmethod
    def initialize(cls, config_path: Optional[str] = None) -> None:
        """初始化日志配置，建议在程序启动时调用"""
        cls._config = LoggerConfig(config_path)
        # 创建日志目录
        log_dir = Path(cls._config.config["log_dir"])
        try:
            log_dir.mkdir(parents=True, exist_ok=True)
        except OSError as e:
            print(f"创建日志目录失败: {e}", file=sys.stderr)

    @classmethod
    def get_logger(cls, name: str, log_file: Optional[str] = None) -> logging.Logger:
        """获取日志实例，确保单例"""
        if name in cls._loggers:
            return cls._loggers[name]

        if not cls._config:
            cls.initialize()  # 使用默认配置初始化

        logger = logging.getLogger(name)
        logger.setLevel(cls._config.log_level)
        logger.propagate = False  # 防止日志重复传播

        # 添加处理器
        cls._add_handlers(logger, log_file)

        cls._loggers[name] = logger
        return logger

    @classmethod
    def _add_handlers(cls, logger: logging.Logger, log_file: Optional[str]) -> None:
        """为日志添加处理器（控制台+文件）"""
        formatter = logging.Formatter(
            fmt=cls._config.config["log_format"],
            datefmt=cls._config.config["date_format"]
        )

        # 控制台处理器
        if cls._config.config["console_output"]:
            console_handler = logging.StreamHandler()
            console_handler.setLevel(cls._config.log_level)
            console_handler.setFormatter(formatter)
            logger.addHandler(console_handler)

        # 文件处理器
        if log_file:
            log_path = Path(cls._config.config["log_dir"]) / log_file
            try:
                # 同时支持按大小和时间轮转（生产环境常用组合）
                file_handler = RotatingFileHandler(
                    filename=log_path,
                    mode="a",
                    maxBytes=cls._config.config["max_bytes"],
                    backupCount=cls._config.config["backup_count"],
                    encoding="utf-8"
                )
                # 额外添加时间轮转（可选，根据业务需求）
                # file_handler = TimedRotatingFileHandler(
                #     filename=log_path,
                #     when=cls._config.config["rotate_when"],
                #     interval=cls._config.config["rotate_interval"],
                #     backupCount=cls._config.config["backup_count"],
                #     encoding="utf-8"
                # )
                file_handler.setLevel(cls._config.log_level)
                file_handler.setFormatter(formatter)
                logger.addHandler(file_handler)
            except OSError as e:
                print(f"创建日志文件处理器失败: {e}", file=sys.stderr)

    @classmethod
    def update_log_level(cls, level: str) -> None:
        """动态更新所有日志的级别"""
        if not cls._config:
            return
        new_level = cls._config.log_level  # 复用转换逻辑
        for logger in cls._loggers.values():
            logger.setLevel(new_level)
            for handler in logger.handlers:
                handler.setLevel(new_level)


# 便捷接口
def get_logger(name: str, log_file: Optional[str] = None) -> logging.Logger:
    if log_file is None:
        log_file = "edu.log"
    return LoggerFactory.get_logger(name, log_file)


# 初始化入口（建议在程序启动时调用）
def init_logger(config_path: Optional[str] = None) -> None:
    LoggerFactory.initialize(config_path)


if __name__ == '__main__':
    # 示例用法
    init_logger("configs/log_config.yaml")  # 可指定配置文件路径
    logger = get_logger("ai_model", "model.log")

    logger.debug("调试信息：模型初始化参数")
    # logger.info("模型加载完成")
    # logger.warning("输入数据格式异常")
    # logger.error("内存溢出", exc_info=True)  # 记录异常堆栈
    # logger.critical("服务崩溃，紧急处理")
    logger = get_logger("app222", "app.log")
    logger.info("应用启动")
